---
title: "Async / Await"
description: "Async / await for asynchronous operations"
canonical: "/docs/manual/async-await"
section: "Language Features"
order: 22
---

{/* This prelude is used in many different followup examples, so we use it to shorten the noise of the example code. */}

<div className="hidden">

```res prelude
@val external fetchUserMail: string => promise<string> = "GlobalAPI.fetchUserMail"
@val external sendAnalytics: string => promise<unit> = "GlobalAPI.sendAnalytics"
```

</div>

{/* See https://github.com/cristianoc/rescript-compiler-experiments/pull/1#issuecomment-1131182023 for all async/await use-case examples */}

# Async / Await

ReScript comes with `async` / `await` support to make asynchronous, `Promise` based code easier to read and write. This feature is very similar to its JS equivalent, so if you are already familiar with JS' `async` / `await`, you will feel right at home.

## How it looks

Let's start with a quick example to show-case the syntax:

<CodeTab labels={["ReScript", "JS Output"]}>

```res
// Some fictive functionality that offers asynchronous network actions
@val external fetchUserMail: string => promise<string> = "GlobalAPI.fetchUserMail"
@val external sendAnalytics: string => promise<unit> = "GlobalAPI.sendAnalytics"

// We use the `async` keyword to allow the use of `await` in the function body
let logUserDetails = async (userId: string) => {
  // We use `await` to fetch the user email from our fictive user endpoint
  let email = await fetchUserMail(userId)

  await sendAnalytics(`User details have been logged for ${userId}`)

  Console.log(`Email address for user ${userId}: ${email}`)
}
```

```js
async function logUserDetails(userId) {
  var email = await GlobalAPI.fetchUserMail(userId);
  await GlobalAPI.sendAnalytics(
    "User details have been logged for " + userId + "",
  );
  console.log("Email address for user " + userId + ": " + email + "");
}
```

</CodeTab>

As we can see above, an `async` function is defined via the `async` keyword right before the function's parameter list. In the function body, we are now able to use the `await` keyword to explicitly wait for a `Promise` value and assign its content to a let binding `email`.

You will probably notice that this looks very similar to `async` / `await` in JS, but there are still a few details that are specific to ReScript. The next few sections will go through all the details that are specific to the ReScript type system.

## Basics

- You may only use `await` in `async` function bodies
- `await` may only be called on a `promise` value
- `await` calls are expressions, therefore they can be used in pattern matching (`switch`)
- A function returning a `promise<'a>` is equivalent to an `async` function returning a value `'a` (important for writing signature files and bindings)
- `promise` values and types returned from an `async` function don't auto-collapse into a "flat promise" like in JS (more on this later)

## Types and `async` functions

### `async` function type signatures

Function type signatures (i.e defined in signature files) don't require any special keywords for `async` usage. Whenever you want to type an `async` function, use a `promise` return type.

```resi
// Demo.resi

let fetchUserMail: string => promise<string>
```

The same logic applies to type definitions in `.res` files:

```res example
// function type
type someAsyncFn = int => promise<int>

// Function type annotation
let fetchData: string => promise<string> = async (userId) => {
  await fetchUserMail(userId)
}
```

**BUT:** When typing `async` functions in your implementation files, you need to omit the `promise<'a>` type:

```res
// This function is compiled into a `string => promise<string>` type.
// The promise<...> part is implicitly added by the compiler.
let fetchData = async (userId: string): string => {
  await fetchUserMail("test")
}
```

For completeness reasons, let's expand the full signature and inline type definitions in one code snippet:

```res
// Note how the inline return type uses `string`, while the type definition uses `promise<string>`
let fetchData: string => promise<string> = async (userId: string): string {
  await fetchUserMail(userId)
}
```

**Note:** In a practical scenario you'd either use a type signature, or inline types, not both at the same time. In case you are interested in the design decisions, check out [this discussion](https://github.com/rescript-lang/rescript-compiler/pull/5913#issuecomment-1359003870).

### Promises don't auto-collapse in async functions

In JS, nested promises (i.e. `promise<promise<'a>>`) will automatically collapse into a flat promise (`promise<'a>`). This is not the case in ReScript. Use the `await` function to manually unwrap any nested promises within an `async` function instead.

```res
let fetchData = async (userId: string): string => {
  // We can't just return the result of `fetchUserMail`, otherwise we'd get a
  // type error due to our function return type of type `string`
  await fetchUserMail(userId)
}
```

## Error handling

You may use `try / catch` or `switch` to handle exceptions during async execution.

```res
// For simulation purposes
let authenticate = async () => {
  JsError.RangeError.throwWithMessage("Authentication failed.")
}

let checkAuth = async () => {
  try {
    await authenticate()
  } catch {
  | JsExn(e) =>
    switch JsExn.message(e) {
    | Some(msg) => Console.log("JS error thrown: " ++ msg)
    | None => Console.log("Some other exception has been thrown")
    }
  }
}
```

Note how we are essentially catching JS errors the same way as described in our [Exception](./exception.mdx#catch-rescript-exceptions-from-js) section.

You may unify error and value handling in a single switch as well:

```res
let authenticate = async () => {
  JsError.RangeError.throwWithMessage("Authentication failed.")
}

let checkAuth = async () => {
  switch await authenticate() {
  | _ => Console.log("ok")
  | exception JsExn(e) =>
    switch JsExn.message(e) {
    | Some(msg) => Console.log("JS error thrown: " ++ msg)
    | None => Console.log("Some other exception has been thrown")
    }
  }
}
```

**Important:** When using `await` with a `switch`, always make sure to put the actual await call in the `switch` expression, otherwise your `await` error will not be caught.

## Piping `await` calls

You may want to pipe the result of an `await` call right into another function.
This can be done by wrapping your `await` calls in a new `{}` closure.

<CodeTab labels={["ReScript", "JS Output"]}>

```res example
@val external fetchUserMail: string => promise<string> = "GlobalAPI.fetchUserMail"

let fetchData = async () => {
  let mail = {await fetchUserMail("1234")}->String.toUpperCase
  Console.log(`All upper-cased mail: ${mail}`)
}
```

```js
async function fetchData(param) {
  var mail = (await GlobalAPI.fetchUserMail("1234")).toUpperCase();
  console.log("All upper-cased mail: " + mail + "");
}
```

</CodeTab>

Note how the original closure was removed in the final JS output. No extra allocations!

## Pattern matching on `await` calls

`await` calls are just another kind of expression, so you can use `switch` pattern matching for more complex logic.

<CodeTab labels={["ReScript", "JS Output"]}>

```res example
@val external fetchUserMail: string => promise<string> = "GlobalAPI.fetchUserMail"

let fetchData = async () => {
  switch (await fetchUserMail("user1"), await fetchUserMail("user2")) {
  | (user1Mail, user2Mail) => {
      Console.log("user 1 mail: " ++ user1Mail)
      Console.log("user 2 mail: " ++ user2Mail)
    }

  | exception JsExn(err) => Console.log2("Some error occurred", err)
  }
}
```

```js
async function fetchData(param) {
  var val;
  var val$1;
  try {
    val = await GlobalAPI.fetchUserMail("user1");
    val$1 = await GlobalAPI.fetchUserMail("user2");
  } catch (raw_err) {
    var err = Caml_js_exceptions.internalToOCamlException(raw_err);
    if (err.RE_EXN_ID === "JsError") {
      console.log("Some error occurred", err._1);
      return;
    }
    throw err;
  }
  console.log("user 1 mail: " + val);
  console.log("user 2 mail: " + val$1);
}
```

</CodeTab>

## `await` multiple promises

We can utilize the `Promise` module to handle multiple promises. E.g. let's use `Promise.all` to wait for multiple promises before continuing the program:

```res
let pauseReturn = (value, timeout) => {
  Promise.make((resolve, _reject) => {
    setTimeout(() => {
      resolve(value)
    }, timeout)->ignore
  })
}

let logMultipleValues = async () => {
  let promise1 = pauseReturn("value1", 2000)
  let promise2 = pauseReturn("value2", 1200)
  let promise3 = pauseReturn("value3", 500)

  let all = await Promise.all([promise1, promise2, promise3])

  switch all {
  | [v1, v2, v3] => Console.log(`All values: ${v1}, ${v2}, ${v3}`)
  | _ => Console.log("this should never happen")
  }
}
```

## JS Interop with `async` functions

`async` / `await` practically works with any function that returns a `promise<'a>` value. Map your `promise` returning function via an `external`, and use it in an `async` function as usual.

Here's a full example of using the MDN `fetch` API, using `async` / `await` to simulate a login:

```res
// A generic Response type for typing our fetch requests
module Response = {
  type t<'data>
  @send external json: t<'data> => promise<'data> = "json"
}

// A binding to our globally available `fetch` function. `fetch` is a
// standardized function to retrieve data from the network that is available in
// all modern browsers.
@val @scope("globalThis")
external fetch: (
  string,
  'params,
) => promise<Response.t<{"token": Nullable.t<string>, "error": Nullable.t<string>}>> =
  "fetch"

// We now use our asynchronous `fetch` function to simulate a login.
// Note how we use `await` with regular functions returning a `promise`.
let login = async (email: string, password: string) => {
  let body = {
    "email": email,
    "password": password,
  }

  let params = {
    "method": "POST",
    "headers": {
      "Content-Type": "application/json",
    },
    "body": Json.stringifyAny(body),
  }

  try {
    let response = await fetch("https://reqres.in/api/login", params)
    let data = await response->Response.json

    switch Nullable.toOption(data["error"]) {
    | Some(msg) => Error(msg)
    | None =>
      switch Nullable.toOption(data["token"]) {
      | Some(token) => Ok(token)
      | None => Error("Didn't return a token")
      }
    }
  } catch {
  | _ => Error("Unexpected network error occurred")
  }
}
```
